const fp = require('lodash/fp');
const { Maybe, Container } = require('./support')
/**
 * 简答题
 * 宏任务、微任务
 * 
 * 在js机制运行代码时，会先运行主线程的任务，然后在运行消息队列中的任务。
 * 首先会执行调用栈中到主线任务，在遇到异步操作，会将异步任务放入消息队列中（webapi），并将异步任务移除调用栈
 * 在遇到宏任务时，会把宏任务放入到Macroqueue中，遇到微任务也会将任务放入到Microqueue
 * 在eventloop主要循环监听调用栈和消息队列中到任务，在调用栈没有任务以后，会将消息队列中到任务一一执行，
 * 在消息队列中到任务，首先会执行微任务，因为仅仅是线程到调配，宏任务一般消耗资源比较大，会后执行。
 * 常见宏任务：setTimeout，setInterval，script
 * 常见微任务：promise、process.nextTick

/**
 * 代码题
 * （一） 使用promise改进代码
 */
const promise = new Promise((resolve, reject) => {
    resolve('hello')
})
promise.then((a) => {
    return a
}).then((a) => {
    const b = 'lagou'
    return a + b
}).then((res) => {
    const c = 'I love you'
    console.log(res + c)
})
/**
 * （二）
 * 练习题1
 */
const cars = [
    {
        name: 'Ferrari FF',
        horsepower: 660,
        dollar_value: 700000,
        in_stock: true
    },
    {
        name: 'Spyker C12 Zagato',
        horsepower: 650,
        dollar_value: 648000,
        in_stock: false
    },
    {
        name: 'Jaguar XKR-S',
        horsepower: 550,
        dollar_value: 132000,
        in_stock: false
    }
]
const isLastInStock = fp.flowRight(fp.curry(fp.prop('in_stock')), fp.last)
console.log(isLastInStock(cars))
/**
 * 练习题2
 */
const getFirstName = fp.flowRight(fp.curry(fp.prop('name')), fp.first)
console.log(getFirstName(cars))
/**
 * 练习题3
 */
let _average = function (xs) {
    return fp.reduce(fp.add, 0, xs) / xs.length
}
const averageDollarValue = fp.flowRight(_average, fp.map(fp.prop('dollar_value')))
console.log(averageDollarValue(cars))
/**
 * 练习题4
 */
let _underscore = fp.replace(/\W+/g, '_')
const sanitizeNames = fp.flowRight(fp.map(_underscore), fp.map(fp.toLower))
console.log(sanitizeNames(['Hello World', 'La Gou', 'Hang Zhou']))

/**
 * 练习1
 * 使用fp.add(x,y) 和 fp.map(f,x)能让functor里值增加的函数ex1
 */
let ex1 = (arr) => {
    return fp.map((item) => {
        return fp.add(item, 1)
    }, arr)
}
let maybe = Maybe.of([5, 6, 1]).map(arr => ex1(arr))
console.log(maybe)

/**
 * 练习2
 * fp.first 获取列表第一个元素
 */

let ex2 = (arr) => {
    return fp.first(arr)
}
let xs = Container.of(['do', 'ray', 'me']).map(value => ex2(value))
console.log(xs)

/**
 * 练习3
 * 使用safeProp 和 fp.first 找到 user的名字的首字母
 */

let safeProp = fp.curry(function (x, o) {
    return Maybe.of(o[x])
})
let user = { id: 2, name: 'Albert' }

let ex3 = (str) => {
    return fp.first(str)
}
let first = safeProp('name',user).map(value=>ex3(value))
console.log(first)

/**
 * 练习4
 * 使用Maybe重写ex4 不用if
 */
let ex4  =  Maybe.of('5').map(value=>parseInt(value))
console.log(ex4)


/**
 * 手写promise
 * 这个promise 我是完全copy的老师的，目前应该把所有思路都理清了，因为工作比较忙，确实没有自己完全手敲一遍，抱歉……
 */

const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败

class MyPromise {
  constructor (executor) {
    try {
      executor(this.resolve, this.reject)
    } catch (e) {
      this.reject(e);
    }
  }
  // promsie 状态 
  status = PENDING;
  // 成功之后的值
  value = undefined;
  // 失败后的原因
  reason = undefined;
  // 成功回调
  successCallback = [];
  // 失败回调
  failCallback = [];

  resolve = value => {
    // 如果状态不是等待 阻止程序向下执行
    if (this.status !== PENDING) return;
    // 将状态更改为成功
    this.status = FULFILLED;
    // 保存成功之后的值
    this.value = value;
    // 判断成功回调是否存在 如果存在 调用
    while(this.successCallback.length) this.successCallback.shift()()
  }
  reject = reason => {
    // 如果状态不是等待 阻止程序向下执行
    if (this.status !== PENDING) return;
    // 将状态更改为失败
    this.status = REJECTED;
    // 保存失败后的原因
    this.reason = reason;
    // 判断失败回调是否存在 如果存在 调用
    while(this.failCallback.length) this.failCallback.shift()()
  }
  then (successCallback, failCallback) {
    // 参数可选
    successCallback = successCallback ? successCallback : value => value;
    // 参数可选
    failCallback = failCallback ? failCallback: reason => { throw reason };
    let promsie2 = new MyPromise((resolve, reject) => {
      // 判断状态
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let x = successCallback(this.value);
            // 判断 x 的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve 
            // 如果是promise对象 查看promsie对象返回的结果 
            // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
            resolvePromise(promsie2, x, resolve, reject)
          }catch (e) {
            reject(e);
          }
        }, 0)
      }else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = failCallback(this.reason);
            // 判断 x 的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve 
            // 如果是promise对象 查看promsie对象返回的结果 
            // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
            resolvePromise(promsie2, x, resolve, reject)
          }catch (e) {
            reject(e);
          }
        }, 0)
      } else {
        // 等待
        // 将成功回调和失败回调存储起来
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              let x = successCallback(this.value);
              // 判断 x 的值是普通值还是promise对象
              // 如果是普通值 直接调用resolve 
              // 如果是promise对象 查看promsie对象返回的结果 
              // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
              resolvePromise(promsie2, x, resolve, reject)
            }catch (e) {
              reject(e);
            }
          }, 0)
        });
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              let x = failCallback(this.reason);
              // 判断 x 的值是普通值还是promise对象
              // 如果是普通值 直接调用resolve 
              // 如果是promise对象 查看promsie对象返回的结果 
              // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
              resolvePromise(promsie2, x, resolve, reject)
            }catch (e) {
              reject(e);
            }
          }, 0)
        });
      }
    });
    return promsie2;
  }
  finally (callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value);
    }, reason => {
      return MyPromise.resolve(callback()).then(() => { throw reason })
    })
  }
  catch (failCallback) {
    return this.then(undefined, failCallback)
  }
  static all (array) {
    let result = [];
    let index = 0;
    return new MyPromise((resolve, reject) => {
      function addData (key, value) {
        result[key] = value;
        index++;
        if (index === array.length) {
          resolve(result);
        }
      }
      for (let i = 0; i < array.length; i++) {
        let current = array[i];
        if (current instanceof MyPromise) {
          // promise 对象
          current.then(value => addData(i, value), reason => reject(reason))
        }else {
          // 普通值
          addData(i, array[i]);
        }
      }
    })
  }
  static resolve (value) {
    if (value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value));
  }
}

function resolvePromise (promsie2, x, resolve, reject) {
  if (promsie2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if (x instanceof MyPromise) {
    // promise 对象
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
}